package ignoreurl

import (
	"encoding/json"
	"strings"
	"sync"

	. "gitee.com/tomatomeatman/golang-repository/bricks/model"
	. "gitee.com/tomatomeatman/golang-repository/bricks/utils/function/app"
	. "gitee.com/tomatomeatman/golang-repository/bricks/utils/function/file"
	. "gitee.com/tomatomeatman/golang-repository/bricks/utils/gorm"
	Log "github.com/cihub/seelog"
)

/**
 * 拦截器忽略路径IgnoreURL表基本业务操作结构体
 */
type IgnoreURLService struct {
	CommonService
}

/**
 * 简版'忽略路径'结构体
 */
type SimpleIgonoreURL struct {
	GsIgnoreUrl    string `json:"sIgnoreUrl"`
	GiMustLogin    int    `json:"iMustLogin"`
	GsOnlyUserType string `json:"sOnlyUserType"`
}

var (
	cacheSetNotLogin    map[string]string //路径缓存(全部)免登录
	cacheSubSetNotLogin map[string]string //路径缓存(仅包含/*的路径)免登录
	cacheSetLogin       map[string]string //路径缓存(全部)必须登录
	cacheSubSetLogin    map[string]string //路径缓存(仅包含/*的路径)必须登录
	checkWrite          sync.Mutex        //保存锁
)

// 初始化
func init() {
	go IgnoreURLService{}.initCacheMap() //初始化缓存
}

/**
 * 验证指定url是否在可忽略的访问路径中(给内部拦截器用,直接返回Boolean)
 * @param sUrl 待检验的url
 * @param isMustLogin 是否必须登录
 * @param sUserType 待检验的用户类型
 * @return
 */
func (iu IgnoreURLService) CheckIgnoreUrl(sUrl string, isMustLogin bool, sUserType string) *MsgEmity {
	if strings.TrimSpace(sUrl) == "" {
		return MsgEmity{}.Err(8001, "请求检验的Url参数为空")
	}

	iu.initCacheMap() //初始化缓存

	if sUserType != "" {
		sUserType = ";" + sUserType + ";"
	} else {
		sUserType = ""
	}

	//iMustLogin = (null == iMustLogin || iMustLogin.intValue() != 1) ? 2 : 1;//1为必须登录

	//--在免登录的明确路径中找--//
	if _, ok := cacheSetNotLogin[sUrl]; ok {
		sUserTypeList := cacheSetNotLogin[sUrl] //权限限定的用户类型集合
		if sUserTypeList == "" {
			return MsgEmity{}.Success(true, "在免登录的免拦截中发现,并且不限定用户")
		}

		if strings.Contains(sUserTypeList, sUserType) {
			return MsgEmity{}.Success(true, "在免登录的免拦截中发现,但是限定了用户") //在免登录的免拦截中发现,但是限定了用户
		}
	}

	//--在免登录的明确路径中中没找到,则可能属于子路径请求--//
	for key, sUserTypeList := range cacheSubSetNotLogin {
		if !strings.Contains(sUrl, key) {
			continue
		}

		if sUserTypeList == "" {
			return MsgEmity{}.Success(true, "在免登录的免拦截中发现,并且不限定用户")
		}

		if strings.Contains(sUserTypeList, sUserType) {
			return MsgEmity{}.Success(true, "在免登录的免拦截中发现,但是限定了用户") //在免登录的免拦截中发现,但是限定了用户
		}
	}

	if !isMustLogin { //未指定要"必须登录"
		return MsgEmity{}.Success(false, "在指定项目中都没有找到可以免拦截的约定") // MsgEmity.err(8003, "在指定项目中都没有找到可以免拦截的约定")//不符合
	}

	//--指定要"必须登录"--//

	for key, sUserTypeList := range cacheSetLogin {
		if sUrl != key {
			continue
		}

		if sUserTypeList == "" {
			return MsgEmity{}.Success(true, "在必须登录的免拦截(明确路径)中发现,并且不限定用户")
		}

		if !strings.Contains(sUserTypeList, sUserType) {
			continue //拦截路径的使用限定了用户类型
		}

		return MsgEmity{}.Success(true, "在必须登录的免拦截(明确路径)中发现,并且限定用户")
	}

	for key, sUserTypeList := range cacheSubSetLogin {
		if !strings.Contains(sUrl, key) {
			continue
		}

		if sUserTypeList == "" {
			return MsgEmity{}.Success(true, "在必须登录的免拦截(子路径)中发现,并且不限定用户")
		}

		if !strings.Contains(sUserTypeList, sUserType) {
			continue
		}

		return MsgEmity{}.Success(true, "在必须登录的免拦截(子路径)中发现,并且限定用户")
	}

	return MsgEmity{}.Success(false, "在指定项目中都没有找到可以免拦截的约定") //不符合
}

/**
* 初始化缓存
* @return
 */
func (iu IgnoreURLService) initCacheMap() {
	if len(cacheSetNotLogin) > 0 {
		return
	}

	checkWrite.Lock()         //加锁
	defer checkWrite.Unlock() //解锁

	if len(cacheSetNotLogin) > 0 {
		return
	}

	filePath := "./temp/cache/IgnoreURL/IgnoreURL.json"
	if (FileUtil{}.IsExist(filePath)) {
		me := FileUtil{}.ReadFromFile(filePath)
		if (me.Gdata != 1001) && (me.Gdata != 1002) { //1001为文件不存在;1002为读取失败
			if !me.Gsuccess {
				Log.Error("读取缓存文件失败")
				return
			}

			jsonStr := me.Gdata.(string)
			data := map[string]map[string]string{}
			err := json.Unmarshal([]byte(jsonStr), &data)
			if err != nil {
				Log.Error("读取缓存内容异常:", err)
				return
			}

			if len(data) > 3 {
				cacheSetNotLogin = data["cacheSetNotLogin"]
				cacheSubSetNotLogin = data["cacheSubSetNotLogin"]
				cacheSetLogin = data["cacheSetLogin"]
				cacheSubSetLogin = data["cacheSubSetLogin"]
				return
			}
		}
	}

	//-- 重建缓存文件 --//
	me := iu.allIgnoreUrlList()
	if !me.Gsuccess {
		return
	}

	cacheSetNotLogin = map[string]string{}    //路径缓存(全部)免登录
	cacheSubSetNotLogin = map[string]string{} //路径缓存(仅包含/*的路径)免登录
	cacheSetLogin = map[string]string{}       //路径缓存(全部)必须登录
	cacheSubSetLogin = map[string]string{}    //路径缓存(仅包含/*的路径)必须登录

	list := me.Gdata.([]SimpleIgonoreURL)
	for _, simpleIgonoreURL := range list {
		sIgnoreUrl := simpleIgonoreURL.GsIgnoreUrl
		if strings.TrimSpace(sIgnoreUrl) == "" {
			continue
		}

		array := strings.Split(sIgnoreUrl, ";")
		for _, val := range array {
			val = strings.TrimSpace(val)
			if val == "" {
				continue
			}

			if simpleIgonoreURL.GiMustLogin != 1 {
				sUserType := ""
				if simpleIgonoreURL.GsOnlyUserType != "" {
					sUserType = ";" + simpleIgonoreURL.GsOnlyUserType + ";"
				}

				cacheSetNotLogin[val] = sUserType
			} else {
				sUserType := ""
				if simpleIgonoreURL.GsOnlyUserType != "" {
					sUserType = ";" + simpleIgonoreURL.GsOnlyUserType + ";"
				}

				cacheSetLogin[val] = sUserType
			}

			if !strings.Contains(val, "/*") { //如果不包含*,说明没有下级
				continue
			}

			val = strings.Replace(val, "/*", "", -1)
			if simpleIgonoreURL.GiMustLogin != 1 { //如果包含*,说明要忽略其下级
				sUserType := ""
				if simpleIgonoreURL.GsOnlyUserType != "" {
					sUserType = ";" + simpleIgonoreURL.GsOnlyUserType + ";"
				}

				cacheSubSetNotLogin[val] = sUserType
			} else {
				sUserType := ""
				if simpleIgonoreURL.GsOnlyUserType != "" {
					sUserType = ";" + simpleIgonoreURL.GsOnlyUserType + ";"
				}

				cacheSubSetLogin[val] = sUserType
			}
		}
	}

	data := map[string]map[string]string{
		"cacheSetNotLogin":    cacheSetNotLogin,
		"cacheSubSetNotLogin": cacheSubSetNotLogin,
		"cacheSetLogin":       cacheSetLogin,
		"cacheSubSetLogin":    cacheSubSetLogin,
	}

	ret_json, _ := json.Marshal(data)
	FileUtil{}.Save(string(ret_json), filePath) //写入文件，以便用户权限验证模块使用
}

/**
* 清理缓存
* @return
 */
func (iu IgnoreURLService) ClearCache() *MsgEmity {
	cacheSetNotLogin = map[string]string{}
	cacheSubSetNotLogin = map[string]string{}
	cacheSetLogin = map[string]string{}
	cacheSubSetLogin = map[string]string{}

	sPath := "./temp/cache/IgnoreURL/IgnoreURL.json"
	FileUtil{}.Del(sPath)

	iu.initCacheMap() //初始化缓存,有必要进行,否则会导致拦截器进入访问时第一个会慢

	return MsgEmity{}.Success(8999, "请求清理缓存成功！")
}

// 所有不拦截路径
func (iu IgnoreURLService) allIgnoreUrlList() *MsgEmity {
	me := iu.allIgnoreUrlListByFile()
	if me.Gsuccess {
		return MsgEmity{}.Success(me.Gdata, "读取配置数据成功")
	}

	return iu.allIgnoreUrlListByDb()
}

// 所有不拦截路径(从数据库获取)
func (iu IgnoreURLService) allIgnoreUrlListByDb() *MsgEmity {
	text := SqlFactory{}.ReplaceVariable(`SELECT sIgnoreUrl, iMustLogin, sOnlyUserType FROM ${BaseSystem}IgnoreURL WHERE iState > 0 ORDER BY iMustLogin`)

	rows, err := SqlFactory{}.GetDB().Raw(text).Rows()

	defer rows.Close()

	if nil != err {
		Log.Error("查询发生异常:", err)
		return MsgEmity{}.Err(7002, "查询发生异常:", err)
	}

	res := SqlFactory{}.ScanRows2mapI(rows)
	if res == nil {
		Log.Error("查询后转换数据发生异常")
		return MsgEmity{}.Err(7003, "查询后转换数据发生异常")
	}

	if len(res) < 1 {
		return MsgEmity{}.Err(7004, "数据不存在！")
	}

	result := []SimpleIgonoreURL{}

	for _, obj := range res {
		result = append(result, SimpleIgonoreURL{
			GsIgnoreUrl:    obj["sIgnoreUrl"].(string),
			GiMustLogin:    obj["iMustLogin"].(int),
			GsOnlyUserType: obj["sOnlyUserType"].(string),
		})
	}

	return MsgEmity{}.Success(result, "查询成功")
}

// 所有不拦截路径(从文件获取)
func (iu IgnoreURLService) allIgnoreUrlListByFile() *MsgEmity {
	result := []SimpleIgonoreURL{}

	me := FileUtil{}.ReadFromFile("./config/InterceptorIgnore.json")
	if me.Gsuccess {
		var list []SimpleIgonoreURL
		err := json.Unmarshal([]byte(me.Gdata.(string)), &list)
		if err != nil {
			Log.Error("InterceptorIgnore.json文件转换出错：", err)
		} else {
			result = append(result, list...)
		}
	}

	me = FileUtil{}.ReadFromFile("./config/InterceptorIgnoreLogined.json")
	if me.Gsuccess {
		var list []SimpleIgonoreURL
		err := json.Unmarshal([]byte(me.Gdata.(string)), &list)
		if err != nil {
			Log.Error("InterceptorIgnoreLogined.json文件转换出错：", err)
		} else {
			result = append(result, list...)
		}
	}

	return MsgEmity{}.Success(result, "读取成功")
}
